#pragma once

#include <iostream>
#include <sys/select.h>
#include "Log.hpp"
#include "Error.hpp"
#include "sock_tcp.hpp"
#include "sock_tcp.hpp"

#define NO_EVENT 0x0
#define READ_EVENT (0x1 << 1)
#define WRITE_EVENT (0x1 << 2)
#define EXCEPET_EVENT (0x1 << 3)

class SelectServer
{
private:
    // typedef
    typedef struct FDEvent
    {
        int fd;
        int event;
        std::string ip;
        uint16_t port;
    } FDEvent_t;
    // const
    static const int N = sizeof(fd_set) * 8;
    static const int defaultPort = 8888;
    static const int defaultFd = -1;

public:
    SelectServer(uint16_t port = defaultPort) : _port(port)
    {
    }
    ~SelectServer()
    {
        _listenSock.Close();
    }

    void init()
    {
        logMessage(Debug, "SelectServer init");

        _listenSock.Socket();
        _listenSock.Bind(_port);
        _listenSock.Listen();

        for (int i = 0; i < N; ++i) // init fd array
        {
            _fdArray[i].fd = defaultFd;
        }
        _fdArray[0].fd = _listenSock.Fd(); // 0 is listen fd
        _fdArray[0].event = READ_EVENT;
        _fdArray[0].ip = "127.0.0.1";
        _fdArray[0].port = _port;

        logMessage(Debug, "SelectServer init success");
    }

    void run()
    {
        logMessage(Debug, "SelectServer run");

        // 1. 这里我们能够直接获取新的链接吗？
        // 2. 最开始的时候，我们的服务器是没有太多的sock的，甚至只有一个sock！listensock
        // 3. 在网络中， 新连接到来被当做 读事件就绪！
        // listensock_.Accept(); 不能！
        while (true)
        {
            // 1. 用户告诉内核，我关心哪些sock的读就绪事件！
            // 1.1 设置我们需要关系的sock的fd_set
            fd_set readfds;
            FD_ZERO(&readfds);
            int maxFd = _fdArray[0].fd;
            for (int i = 0; i < N; ++i)
            {
                if (_fdArray[i].fd == defaultFd) // 无效的fd
                {
                    continue;
                }
                FD_SET(_fdArray[i].fd, &readfds);
                // maxFd满足select的第一个参数需求 -- 最大的fd + 1
                maxFd = maxFd > _fdArray[i].fd ? maxFd : _fdArray[i].fd;
            }
            // 1.2 调用select
            int n = select(maxFd + 1, &readfds, nullptr, nullptr, nullptr); // 阻塞监听，n表示就绪的fd的数量
            // 2. 内核告诉我们哪些sock的读就绪了！-- 处理读取事件
            if (n > 0)
            {
                logMessage(Debug, "select read event ...");
                handlerEvent(readfds);
            }
            else if (n == 0)
            {
                logMessage(Debug, "select timeout ...");
                continue;
            }
            else
            {
                logMessage(Error, "select error:%s [code: %d]", strerror(errno), errno);
                continue;
            }
        }
    }

private:
    void handlerEvent(const fd_set &readfds)
    {
        logMessage(Debug, "handlerEvent ...");
        // 2.1 遍历fd_array，找到就绪的fd
        for (int i = 0; i < N; ++i)
        {
            int fd = _fdArray[i].fd;
            if (fd == defaultFd)
            {
                continue;
            }
            // 2.2 判断fd是否在readfds中
            if (FD_ISSET(fd, &readfds))
            {
                // 2.3 在readfds中，就绪了！
                // 分类处理
                if (fd == _listenSock.Fd()) // listensock
                {
                    accepter();
                }
                else // i client sock
                {
                    serverIO(i);
                }
            }
        }
    }
    void accepter()
    {
        // 走到这里，说明listensock就绪了！调用accept是非阻塞的
        // 1. 调用accept
        std::string clientIP;
        uint16_t clientPort;
        int newsockfd = _listenSock.Accept(&clientIP, &clientPort);
        // 2. 将newsockfd添加到fd_array中
        bool flag = false;
        for (int i = 0; i < N; ++i)
        {
            if (_fdArray[i].fd == defaultFd)
            {
                _fdArray[i].fd = newsockfd;
                _fdArray[i].event = READ_EVENT;
                _fdArray[i].ip = clientIP;
                _fdArray[i].port = clientPort;
                logMessage(Info, "new client:[%s:%d] connect success", clientIP.c_str(), clientPort);
                flag = true;
                break;
            }
        }
        if (!flag)
        {
            logMessage(Warning, "fd_array is full");
            close(newsockfd);
        }
    }

    void serverIO(int i)
    {
        // 走到这里，说明i client sock有数据到来了，调用recv是非阻塞的！
        // 1. 从fdArray中取出对应的信息
        int newsockfd = _fdArray[i].fd;
        // 2. recv
        std::string buf = _listenSock.Recv(newsockfd); // TODO
        if (buf == "")
        {
            // 2.1 对端关闭
            close(newsockfd);
            _fdArray[i].fd = defaultFd;
            _fdArray[i].event = NO_EVENT;
            _fdArray[i].ip = "";
            _fdArray[i].port = 0;
            logMessage(Info, "client shutdown: %d", newsockfd);
        }
        else
        {
            // 2.2 正常接收
            // 3. send
            _listenSock.Send(newsockfd, buf); // TODO 
        }
    }

private:
    uint16_t _port;
    SockTCP _listenSock;
    FDEvent_t _fdArray[N];
};
